from django.shortcuts import render
import json
from django.views import View
from django.http import JsonResponse
from goods.models import SKU
from django_redis import get_redis_connection
from meiduo_mall.utils.cookiesecret import CookieSecret


class CartsView(View):
    """购物车的增加、查询、修改、删除"""

    # 添加购物车
    def post(self, request):
        # 1、提取参数
        json_dict = json.loads(request.body.decode("utf-8"))
        sku_id = json_dict.get("sku_id")
        count = json_dict.get("count")
        selected = json_dict.get("selected", True)

        # 2、校验参数
        if not all([sku_id, count]):
            return JsonResponse({'code': 400, 'errmsg': '缺少必要字段'})
        try:
            sku = SKU.objects.get(id=sku_id, is_launched=True)
            count = int(count)
        except Exception as e:
            return JsonResponse({'code': 400, 'errmsg': '参数有误'})
        # 判读selected类型，必须是bool类型(True or False)
        if not isinstance(selected, bool):
            return JsonResponse({'code': 400, 'errmsg': '参数selected有误'})

        # 3、业务数据处理
        user = request.user
        # 3.1、TODO：登陆用户 —— 存入redis
        if user.is_authenticated:
            redis_conn = get_redis_connection("carts")  # 4号库
            # (1)、存哈希对象 —— 商品和数量
            # .hset函数给redis哈希对象新建键值对是"覆盖"操作
            # redis_conn.hset('carts_%s' % user.id, sku_id, count)
            # .hincrby函数给redis哈希对象的键的值进行"累加"操作
            redis_conn.hincrby("carts_%s" % user.id, sku_id, count)
            # (2)、存集合 —— 记录选中状态
            if selected:
                redis_conn.sadd("selected_%s" % user.id, sku_id)
            else:
                redis_conn.srem("selected_%s" % user.id, sku_id)
            return JsonResponse({'code': 0, 'errmsg': '购物车信息存入redis成功'})
        # 3.2、TODO：未登陆用户 —— 存入cookie
        else:
            # (1)、当前请求cookie里面是否已经有购物车数据了
            cookie_str = request.COOKIES.get("carts")   # carts是存入cookie的购物车数据的key
            if cookie_str:
                # cookie有购物车数据，需要解密得到购物车字典数据
                cart_dict = CookieSecret.loads(cookie_str)
            else:
                # cookie中没有购物车数据
                cart_dict = {}
            # (2)、往购物车字典中新增当前新加入的购物车数据
            if sku_id in cart_dict:
                # sku_id存在，则累加count
                cart_dict[sku_id]["count"] += count
                cart_dict[sku_id]["selected"] = selected
            else:
                # sku_id不存在，则新建键值对
                cart_dict[sku_id] = {'count': count, 'selected': selected}
            # (3)、把最新的cookie购物车字典数据，编码成字符串写入cookie
            cookie_str = CookieSecret.dumps(cart_dict)
            response = JsonResponse({'code': 0, 'errmsg': '购物车信息存入cookie成功'})
            response.set_cookie('carts', cookie_str, max_age=3600 * 24 * 14)
            return response

    # 查询购物车，展示购物车
    def get(self, request):
        # 后续为了方便业务数据组织，从redis或者cookie读取的购物车数据，统一组成字典数据
        # 约定一个变量，其值的格式是 "购物车字典数据 {1: {"count": 5, "selected": True}}"
        cart_dict = {}
        user = request.user
        # TODO：登陆用户 —— 查询Redis购物车
        if user.is_authenticated:
            redis_conn = get_redis_connection("carts")
            # (1)、读取购物车商品数量，哈希数据 === redis_carts = {b'1': b'5', b'2': b'3'}
            redis_carts = redis_conn.hgetall("carts_%s" % user.id)
            # (2)、购物车选中状态，集合数据 === redis_selected = [b'1', b'2']
            redis_selected = redis_conn.smembers("selected_%s" % user.id)
            # (3)、把redis购物车数据，转化成购物车字典数据
            sku_ids = redis_carts.keys()
            for sku_id in sku_ids:
                # sku_id = b'1' 需转换
                cart_dict[int(sku_id)] = {"count": int(redis_carts[sku_id]), "selected": sku_id in redis_selected}
        # TODO：未登陆用户 —— 查询cookie购物车并解码
        else:
            cookie_str = request.COOKIES.get("carts")
            if cookie_str:
                cart_dict = CookieSecret.loads(cookie_str)
            else:
                cart_dict = {}

        # 读取mysql获取sku商品的详细信息，构建响应
        sku_ids = cart_dict.keys()  # [1,2] 列表形式
        cart_skus = []
        for sku_id in sku_ids:
            sku = SKU.objects.get(id=sku_id, is_launched=True)
            cart_skus.append({
                "id": sku.id,
                "name": sku.name,
                'default_image_url': sku.default_image.url,
                'price': sku.price,
                'count': cart_dict[sku_id]["count"],
                'selected': cart_dict[sku_id]["selected"],
            })
        return JsonResponse({'code': 0, 'errmsg': '展示购物车成功', 'cart_skus': cart_skus})

    # 修改购物车，更新购物车
    def put(self, request):
        # 1、提取参数
        json_dict = json.loads(request.body.decode("utf-8"))
        sku_id = json_dict.get("sku_id")
        count = json_dict.get("count")
        selected = json_dict.get("selected", True)

        # 2、校验参数
        if not all([sku_id, count]):
            return JsonResponse({'code': 400, 'errmsg': '缺少必要参数'})
        try:
            sku = SKU.objects.get(id=sku_id, is_launched=True)
        except Exception as e:
            return JsonResponse({'code': 400, 'errmsg': 'sku不存在'})
        # 判读selected类型，必须是bool类型(True or False)
        if not isinstance(selected, bool):
            return JsonResponse({'code': 400, 'errmsg': '参数有误'})

        # 3、业务数据处理
        user = request.user
        # TODO：登陆用户，修改redis购物车
        if user.is_authenticated:
            redis_conn = get_redis_connection("carts")
            # (1)、修改商品数量 —— 哈希对象设置键值对默认覆盖原值
            redis_conn.hset("carts_%s" % user.id, sku_id, count)
            # (2)、修改选中状态
            if selected:
                redis_conn.sadd("selected_%s" % user.id, sku_id)
            else:
                redis_conn.srem("selected_%s" % user.id, sku_id)
            return JsonResponse({
                'code': 0,
                'errmsg': '修改购物车redis数据成功',
                'cart_sku': {'id': sku_id, 'count': count, 'selected': selected}
            })
        # TODO：未登陆用户，修改cookie购物车
        else:
            # (1)、读取cookie购物车字典数据
            cookie_str = request.COOKIES.get("carts")
            if cookie_str:
                cart_dict = CookieSecret.loads(cookie_str)
            else:
                cart_dict = {}
            # (2)、修改为新数据
            if sku_id in cart_dict:
                cart_dict[sku_id]["count"] = count
                cart_dict[sku_id]["selected"] = selected
            # (3)、重新加密写入cookie
            cookie_str = CookieSecret.dumps(cart_dict)
            response = JsonResponse({
                'code': 0,
                'errmsg': '修改购物车cookie数据成功',
                'cart_sku': {'id': sku_id, 'count': count, 'selected': selected}
            })
            # 重新写入购物车cookie数据
            response.set_cookie("carts", cookie_str)
            return response

    # 删除购物车
    def delete(self, request):
        # 1、提取参数
        json_dict = json.loads(request.body.decode("utf-8"))
        sku_id = json_dict.get("sku_id")

        # 2、校验参数
        if sku_id is None:
            return JsonResponse({'code': 400, 'errmsg': '缺少必要参数'})
        try:
            sku = SKU.objects.get(id=sku_id, is_launched=True)
        except Exception as e:
            return JsonResponse({'code': 400, 'errmsg': 'sku不存在'})

        # 3、业务数据处理
        user = request.user
        # TODO：登陆用户，删除redis购物车指定sku商品数据
        if user.is_authenticated:
            redis_conn = get_redis_connection("carts")
            # (1)、把sku_id从哈希对象中删除
            redis_conn.hdel("carts_%s" % user.id, sku_id)
            # (2)、把sku_id从集合中删除
            redis_conn.srem("selected_%s" % user.id, sku_id)
            return JsonResponse({'code': 0, 'errmsg': '删除购物车redis数据成功'})
        # TODO：未登陆用户，删除cookie购物车指定sku商品数据
        else:
            # (1)、读取cookie购物车字典数据
            cookie_str = request.COOKIES.get("carts")
            if cookie_str:
                carts_dict = CookieSecret.loads(cookie_str)
            else:
                carts_dict = {}
            # (2)、删除指定sku_id
            if sku_id in carts_dict:
                # del cart_dict[sku_id] 删除字典的键值对
                carts_dict.pop(sku_id)
            # (3)、重新加密设置在cookie中
            cookie_str = CookieSecret.dumps(carts_dict)
            response = JsonResponse({'code': 0, 'errmsg': '删除购物车cookie数据成功'})
            response.set_cookie("carts", cookie_str)
            return response


class CartsSelectAllView(View):
    """全选购物车"""

    def put(self, request):
        # 1、接收参数
        json_dict = json.loads(request.body.decode("utf-8"))
        selected = json_dict.get("selected")

        # 2、校验参数
        # 判读selected类型，必须是bool类型(True or False)
        if not isinstance(selected, bool):
            return JsonResponse({'code': 400, 'errmsg': '参数有误'})

        # 3、业务数据处理
        # 判断用户是否登录
        user = request.user
        # TODO:用户已登录，操作redis购物车
        if user.is_authenticated:
            redis_conn = get_redis_connection("carts")
            # (1)、获取所有sku商品 --- redis_carts = {b'1':b'5', b'2': b'6'}
            redis_carts = redis_conn.hgetall("carts_%s" % user.id)
            # (2)、把所有sku商品的id加入集合 --- sku_ids = [b'1', b'2']
            sku_ids = redis_carts.keys()
            if selected:
                # 把购物车所有商品id加入集合 --- *sku_ids对列表进行拆包：(*sku_ids) = (b'1', b'2')
                redis_conn.sadd("selected_%s" % user.id, *sku_ids)
            else:
                # 把购物车所有商品id从集合中剔除
                redis_conn.srem("selected_%s" % user.id, *sku_ids)
            return JsonResponse({'code': 0, 'errmsg': 'redis购物车全选成功'})

        # TODO:用户未登录，操作cookie购物车
        else:
            # (1)、读取cookie购物车字典
            cookie_str = request.COOKIES.get("carts")
            if cookie_str is None:
                cart_dict = {}
            else:
                cart_dict = CookieSecret.loads(cookie_str)
            # (2)、把购物车字典中所有商品的selected进行操作 --- cart_dict = {11: {'count':5, 'selected': False},...}
            sku_ids = cart_dict.keys()
            for sku_id in sku_ids:
                # 前端传来的selected是True表示全选，为False表示全部取消
                cart_dict[sku_id]["selected"] = selected
            # (3)、加密新字典写入cookie
            cookie_str = CookieSecret.dumps(cart_dict)
            response = JsonResponse({'code': 0, 'errmsg': 'cookis购物车全选成功'})
            response.set_cookie("carts", cookie_str)
            return response


class CartsSimpleView(View):
    """简单购物车展示"""

    def get(self, request):
        user = request.user

        # 初始化一个空的购物车字典数据，用于记录后续redis或者cookie中读取的购物车
        # cart_dict = {1: {"count": 5, "selected": True}}
        cart_dict = {}

        # TODO:判断用户是否登录
        if user.is_authenticated:
            # 登陆读取redis，展示redis购物车
            redis_conn = get_redis_connection("carts")
            redis_carts = redis_conn.hgetall("carts_%s" % user.id)      # redis_carts = {b'11': b'5', b'6': b'3'}
            redis_selected = redis_conn.smembers("selected_%s" % user.id)   # redis_selected = [b'11', b'6']
            # 构建统一约定好的购物车字典数据 —— 只处理选中
            sku_ids = redis_carts.keys()    # [b'1']
            for sku_id in sku_ids:
                if sku_id in redis_selected:    # b'1' in [b'1']
                    cart_dict[int(sku_id)] = {
                        "count": int(redis_carts[sku_id]),
                        "selected": sku_id in redis_selected
                    }
        else:
            # 未登陆读取cookie，展示cookie购物车
            cookie_str = request.COOKIES.get("carts")
            if cookie_str is None:
                cart_dict = {}
            else:
                cart_dict = CookieSecret.loads(cookie_str)

            # 我们把keys返回的迭代器强转为列表，后续遍历的是列表中保存的所有选中的sku_id。
            # 这样我们就可以改变购物车字典的长度 —— 删除key
            sku_ids = list(cart_dict.keys())    # 强转为列表
            for sku_id in sku_ids:
                # sku商品在cookie购物车字典中未选项时，删除字典中该sku_id
                if not cart_dict[sku_id]["selected"]:
                    cart_dict.pop(sku_id)

        # 从mysql中读取sku详细信息，并构建响应
        cart_skus = []
        sku_ids = cart_dict.keys()  # 经过上面的操作后都是被选定的商品
        for sku_id in sku_ids:
            sku = SKU.objects.get(id=sku_id)
            cart_skus.append({
                'id': sku.id,
                'name': sku.name,
                'count': cart_dict[sku_id]['count'],
                'default_image_url': sku.default_image.url
            })
        return JsonResponse({'code': 0,  'errmsg': '展示简单购物车成功', 'cart_skus': cart_skus})
